/*
	This file is part of Vajolet.

    Vajolet is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Vajolet is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Vajolet.  If not, see <http://www.gnu.org/licenses/>
*/

#ifndef _MOVEGEN
#define _MOVEGEN

#include "board.h"
#include "move.h"
#include "search.h"

#define MOVEGEN_VERSION "3"
// ver 3: aggiunta material key e piececount


// Macro's to define sliding attacks:
#define RANKATTACKS(a)		(RANK_ATTACKS[(a)][((occupiedSquares & MG_RANKMASK[(a)]) >> RANKSHIFT[(a)])])
#define FILEATTACKS(a)		(FILE_ATTACKS[(a)][((occupiedSquares & MG_FILEMASK[(a)]) * MG_FILEMAGIC[(a)]) >> 57])
#define SLIDEA8H1ATTACKS(a)	(DIAGA8H1_ATTACKS[(a)][((occupiedSquares & MG_DIAGA8H1MASK[(a)]) * MG_DIAGA8H1MAGIC[(a)]) >> 57])
#define SLIDEA1H8ATTACKS(a)	(DIAGA1H8_ATTACKS[(a)][((occupiedSquares & MG_DIAGA1H8MASK[(a)]) * MG_DIAGA1H8MAGIC[(a)]) >> 57])
#define ROOKATTACKS(a)	(RANKATTACKS(a) | FILEATTACKS(a))
#define BISHOPATTACKS(a)	(SLIDEA8H1ATTACKS(a) | SLIDEA1H8ATTACKS(a))

#define ROOKMOVES(a)		(ROOKATTACKS(a)& targetBitmap)
#define BISHOPMOVES(a)		(BISHOPATTACKS(a)& targetBitmap)
#define QUEENMOVES(a)		((BISHOPATTACKS(a) | ROOKATTACKS(a))& targetBitmap)

extern score threatGain;

class Movegen{
private:
	// pool di mosse usato per velocizzare l'esecuzione senza dov er allocare in continuo memoria
	static Move quietMovesPool[MAX_PLY*2][MAX_PLIES_FOR_POSITION];
	static Move captureMovesPool[MAX_PLY*2][MAX_PLIES_FOR_POSITION];
	// indice della prossima moveList
	static int nextPool;
public:
	Move * captureMoves;
	Move * quietMoves;

private:
	Move excludeMove;
	Move threatMove;
	Move TTmove;

	Move temp;
public:
	score captureThreshold;
	int captureIndex;
	int quietIndex;
	int killerCounter;

	int captureLastMove;
	int quietLastMove;
	int d;
	bool color;

	
	void genCapture(bool allowUnderPromotion=false);
	void genQuiet();
	void genEvasion();
	void genEvasionCapture(bool allowUnderPromotion=false);
	void genEvasionQuiet();

	
public:
	void genAll();

	unsigned short killermoves[2];
	unsigned short refutation;
	enum eGetMovePhase{
		getTT=0,
		getGenerateMoves,
		getGoodCapture,
		getKillers,
		getRefutation,
		getGenerateQuiet,
		getNonCapture,
		getBadCapture,
		perft,
		getTTProbCut,
		getGenerateCaptureProbCut,
		getGoodCaptureProbCut,
		getBadCaptureProbCut,
		getTTQsearch,
		getGenerateCaptureQsearch,
		getGoodCaptureQsearch,
		getBadCaptureQsearch,
		getTTroot,
		generateRoot,
		getOtherRoot

	}getMovePhase;

	bool isValidMove(unsigned short packed);
	bool isValidMove(Move m);
	
	inline void setThreatMove(Move t){
		threatMove.packed=t.packed;
	}
	inline void setTTmove(Move t){
		TTmove.packed=t.packed;
	}
	inline void setExcludeMove(Move t){
		excludeMove.packed=t.packed;
	}
	

#ifdef _GET_NEXT_STATS
	static unsigned long getNextCalls;
	static unsigned long getTTCalls;
	static unsigned long getGCCalls;
	static unsigned long getKICalls;
	static unsigned long getBCCalls;
	static unsigned long getNCCalls;

	static void initStats(){
		getNextCalls=0;
		getTTCalls=0;
		getGCCalls=0;
		getKICalls=0;
		getBCCalls=0;
		getNCCalls=0;
	}
	static void printStats(){
		if(getNextCalls){
			std::cout<<"----STATS-------"<<std::endl;
			std::cout<<"TT:"<<((float)getTTCalls)/getNextCalls*100<<"%"<<std::endl;
			std::cout<<"GC:"<<((float)getGCCalls)/getNextCalls*100<<"%"<<std::endl;
			std::cout<<"KI:"<<((float)getKICalls)/getNextCalls*100<<"%"<<std::endl;
			std::cout<<"BC:"<<((float)getBCCalls)/getNextCalls*100<<"%"<<std::endl;
			std::cout<<"NC:"<<((float)getNCCalls)/getNextCalls*100<<"%"<<std::endl;
		}
	}
#endif


	Movegen(int ply,score refinedValue=-LARGE_NUMBER,unsigned short int depth=100, score beta=LARGE_NUMBER){
		
		threatMove.clear();
		
		captureMoves=captureMovesPool[nextPool];
		quietMoves=quietMovesPool[nextPool];
		nextPool++;

		captureThreshold=0;

		captureLastMove=0;
		quietLastMove=0;

		captureIndex=0;
		quietIndex=0;

		if(ply==0){
			getMovePhase=getTTroot;
		}else{
			getMovePhase=getTT;
		}
		color=(board.nextMove==Board::black);
		killermoves[0]=Search::killermoves[color][ply][0].packed;
		killermoves[1]=Search::killermoves[color][ply][1].packed;
		refutation=Search::refutationMoves[ 1-color ][ board.square[board.gameLine[board.gameLineIndex-1].move.to]] [board.gameLine[board.gameLineIndex-1].move.to].packed;
		d=depth;
		killerCounter=0;
		
		
		// Consider sligtly negative captures as good if at low depth and far from beta
		if (refinedValue < beta - 100 && depth < 3){
			captureThreshold = -100;
		}// Consider negative captures as good if still enough to reach beta
		else if (refinedValue > beta){
          captureThreshold = beta - refinedValue;
		}

		
	}

	~Movegen(){
		nextPool--;
	}

	

	
	void genMoves(){
		if(board.isOwnKingAttacked()){
			genEvasion();
		}else{
			genAll();
		}
	}
	void genCaptureGeneric(bool allowUnderPromotion= false){
		if(board.isOwnKingAttacked()){
			genEvasionCapture(allowUnderPromotion);
		}else{
			genCapture(allowUnderPromotion);
		}
	}
	void genQuietGeneric(){
		if(board.isOwnKingAttacked()){
			genEvasionQuiet();
		}else{
			genQuiet();
		}
	}
	
	bool getNext(Move & m);

	bool isMoveInTheList(Move & m) const {
		for (int i=0;i<captureLastMove;i++){
			if(captureMoves[i].packed== m.packed){
				return true;
			}
		}
		for (int i=0;i<quietLastMove;i++){
			if(quietMoves[i].packed== m.packed){
				return true;
			}
		}
		return false;
	}
	
	void exludeMoveFromQuiet(unsigned short m){
		if(!m)
			return;
		for(int i=0;i<quietLastMove;i++){
			if(quietMoves[i].packed == m){
				for(int n=i;n<quietLastMove-1;n++){
					quietMoves[n+1].copyTo(quietMoves[n]);
				}
				quietLastMove--;
				return;
			}
		}
	}
	void exludeMoveFromQuiet(Move & m){
		if(m.packed!=0 && m.isCapture()){
			return;
		}
		exludeMoveFromQuiet(m.packed);
	}
	void exludeMoveFromCapture(Move & m){
		if(m.packed==0 || (getMovePhase!=generateRoot &&  m.packed!=0 && !m.isCapture())){
			return;
		}
		for(int i=0;i<captureLastMove;i++){
			if(captureMoves[i].packed == m.packed){
				for(int n=i;n<captureLastMove-1;n++){
					captureMoves[n+1].copyTo(captureMoves[n]);
				}
				captureLastMove--;
				return;
			}
		}
	}
};

#endif
